<template>
  <el-form
    :model="form"
    ref="form"
    :rules="rules"
    :label-width="labelWidth"
    :disabled="disabled"
    :inline="inline"
    label-position="top"
    class="base-form-wrap"
  >
    <template v-for="field in config">
      <el-form-item
        :key="field.prop"
        :label="field.label"
        :prop="field.prop"
        v-if="field.show != false"
      >
        <template v-if="field.type == 'select'">
          <el-select
            v-model="form[field.prop]"
            :disabled="getDisabled(field)"
            :placeholder="getPlaceholder(field)"
            clearable
            filterable
            v-bind="field.attr"
            @change="handleInput(field)"
          >
            <el-option
              v-for="item in options[field.prop]"
              :key="item.value"
              :label="item.label"
              :value="item.value"
            >
            </el-option>
          </el-select>
        </template>
        <template v-else-if="field.type == 'selects'">
          <el-select
            v-model="form[field.prop]"
            :disabled="getDisabled(field)"
            :placeholder="getPlaceholder(field)"
            multiple
            clearable
            filterable
            v-bind="field.attr"
            @change="handleInput(field)"
          >
            <el-option
              v-for="item in options[field.prop]"
              :key="item.value"
              :label="item.label"
              :value="item.value"
            >
            </el-option>
          </el-select>
        </template>
        <template v-else-if="field.type == 'cascader'">
          <el-cascader
            :options="options[field.prop]"
            :disabled="getDisabled(field)"
            v-model="form[field.prop]"
            :placeholder="getPlaceholder(field)"
            clearable
            filterable
            :show-all-levels="false"
            :props="{
              expandTrigger: 'hover',
              multiple: true,
              checkStrictly: true,
              ...field.attr,
            }"
            @change="handleInput(field)"
          >
          </el-cascader>
        </template>
        <template v-else-if="field.type == 'date'">
          <el-date-picker
            v-model="form[field.prop]"
            :disabled="getDisabled(field)"
            type="date"
            placeholder="请选择日期"
            align="center"
            value-format="yyyy-MM-dd"
            v-bind="field.attr"
            @change="handleInput(field)"
          >
          </el-date-picker>
        </template>
        <template v-else-if="field.type == 'daterange'">
          <el-date-picker
            v-model="form[field.prop]"
            :disabled="getDisabled(field)"
            type="daterange"
            placeholder="请选择日期范围"
            align="center"
            value-format="yyyy-MM-dd"
            v-bind="field.attr"
            @change="handleInput(field)"
          >
          </el-date-picker>
        </template>
        <template v-else-if="field.type == 'input'">
          <el-input
            v-model="form[field.prop]"
            :disabled="getDisabled(field)"
            :placeholder="getPlaceholder(field)"
            v-bind="field.attr"
            @change="handleInput(field)"
          >
          </el-input>
        </template>
        <template v-else-if="field.type == 'number'">
          <el-input
            v-model.number="form[field.prop]"
            :disabled="getDisabled(field)"
            :placeholder="getPlaceholder(field)"
            v-bind="field.attr"
            @change="handleInput(field)"
          >
          </el-input>
        </template>
        <template v-else-if="field.type == 'textarea'">
          <el-input
            v-model.trim="form[field.prop]"
            :disabled="getDisabled(field)"
            type="textarea"
            show-word-limit
            :placeholder="getPlaceholder(field)"
            v-bind="field.attr"
            @change="handleInput(field)"
          >
          </el-input>
        </template>
        <template v-else-if="field.type == 'check'">
          <el-checkbox-group
            v-model="form[field.prop]"
            :disabled="getDisabled(field)"
            v-bind="field.attr"
            @change="handleInput(field)"
          >
            <el-checkbox
              v-for="item in options[field.prop]"
              :key="item.key"
              :label="item.label"
            >
              {{ item.value }}
            </el-checkbox>
          </el-checkbox-group>
        </template>
        <template v-else-if="field.type == 'radio'">
          <el-radio-group
            v-model="form[field.prop]"
            :disabled="getDisabled(field)"
            v-bind="field.attr"
            @change="handleInput(field)"
          >
            <el-radio
              v-for="item in options[field.prop]"
              :key="item.key"
              :label="item.label"
            >
              {{ item.value }}
            </el-radio>
          </el-radio-group>
        </template>
        <template v-else-if="field.type == 'switch'">
          <el-switch
            v-model="form[field.prop]"
            :active-value="1"
            :inactive-value="0"
            @change="handleInput(field)"
          >
          </el-switch>
        </template>
        <template v-else-if="field.type == 'btns'">
          <template v-for="btn in field.btnConfig">
            <el-button
              :key="btn.invalid"
              :disabled="btn.disabled"
              :type="btn.type"
              @click="handleBtnAction(btn)"
              v-bind="btn.attr"
              >{{ btn.label }}</el-button
            >
          </template>
        </template>
        <p>{{ field.desc }}</p>
      </el-form-item>
      <br v-if="field.break" />
    </template>
  </el-form>
</template>

<script>
export default {
  name: "BaseForm",
  props: {
    config: {
      type: Array,
      default: () => [],
    },
    rules: {
      type: Object,
      default: () => {},
    },
    info: {
      type: Object,
      default: () => {},
    },
    inline: {
      type: Boolean,
      default: false,
    },
    disabled: {
      type: Boolean,
      default: false,
    },
    labelWidth: {
      type: String,
      default: "100px",
    },
  },
  model: {
    prop: "info",
    event: "change",
  },
  data() {
    return {
      form: {},
      options: {},
    };
  },
  methods: {
    init() {
      this.config.forEach((field) => {
        if (
          ["select", "selects", "cascader", "check", "radio"].includes(
            field.type
          )
        ) {
          this.$set(this.options, field.prop, []);
          if (field.options) {
            this.options[field.prop] = field.options;
          } else if (field.optionsApi) {
            this.getOptions(field);
          }
        }
      });
    },
    handleInput(field) {
      if (["select", "selects"].includes(field.type)) {
        const obj = field.options.find(
          (opt) => opt.value == this.form[field.prop]
        );
        this.$emit("handleInput", field.prop, this.form[field.prop], obj);
      } else {
        this.$emit("handleInput", field.prop, this.form[field.prop]);
      }
    },
    getOptions(field) {
      if (this.options[field.prop].length == 0) {
        this.$api
          .get(`/system/dict/data/type${field.optionsApi}`)
          .then((res) => {
            this.options[field.prop] = res.data;
          });
      }
    },
    handleBtnAction(btn) {
      this.$emit(btn.invalid);
    },
    getDisabled(field) {
      if (Object.hasOwn(field, "disabled")) {
        const type = typeof field.disabled;
        if (type == "boolean") {
          return field.disabled;
        } else if (type == "function") {
          return field.disabled(this.form);
        }
      }
    },
    getPlaceholder(field) {
      if (this.getDisabled(field)) {
        return "";
      }
      let placeholder = "请";
      if (["input", "textarea", "number"].includes(field.type)) {
        placeholder += "输入";
      } else if (["select", "selects", "cascader"].includes(field.type)) {
        placeholder += "选择";
      }
      placeholder += field.label;
      return placeholder;
    },
    validate() {
      return this.$refs.form.validate();
    },
    resetFields() {
      return this.$refs.form.resetFields();
    },
  },
  watch: {
    form: {
      handler(nv, ov) {
        if (!this._.isEqual(nv, ov)) {
          this.$emit("change", nv);
        }
      },
      deep: true,
      // immediate: true,
    },
    info: {
      handler(data) {
        this.form = data;
      },
      deep: true,
      immediate: true,
    },
    config: {
      handler(data) {
        this.init();
      },
      deep: true,
      immediate: true,
    },
  },
};
</script>

<style lang="scss" scoped>
.base-form-wrap {
  padding: 1em;
  .el-select,
  .el-input-number,
  .el-cascader,
  .el-input {
    min-width: 220px;
  }
  .el-textarea {
    width: 100%;
  }
}
</style>
